using AvaVSExt.Attributes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace AvaVSExt
{
    public partial class XamlPropValue
    {
        public string Property { get; set; }
        public string Value { get; set; }
    }

    public partial class Program
    {
        [VSCommand("test")]
        public static void Test()
        {
            var xml = @"        <Border
            Padding=""5""
            Background=""Gray""
            CornerRadius=""10"">
            <TextBlock
                FontSize=""14""
                Foreground=""red""
                Text=""1111"" />
        </Border>
";

            var xmldoc = new XmlDocument();
            xmldoc.LoadXml(xml);

            var ele = ExtractStyle.ExtractNodeStyle(xmldoc.DocumentElement);
            var result = ele.OuterXml;
        }
    }

    public class ExtractStyle
    {
        public static XmlElement ExtractNodeStyle(XmlElement ele)
        {
            var isControl = IsControl(ele);
            if (isControl == false) return ele;

            var tobeStyledList = new List<XamlPropValue>();
            var atts = ele.Attributes.OfType<XmlAttribute>().ToArray();
            
            foreach (var att in atts)
            {
                var attName = att.Name;
                if (IsIgnoreAttribute(attName)) continue;
                ele.Attributes.Remove(att);
                tobeStyledList.Add(new XamlPropValue() { Property = att.Name, Value = att.Value });
            }


            if(tobeStyledList.Count != 0)
            {
                var styleNode = GetOrCreateStyle(ele);
                styleNode.AppendChild(CreateStyle(ele.OwnerDocument,ele.Name, tobeStyledList));
            }
            var children = ele.ChildNodes.OfType<XmlElement>().ToArray();

            foreach (var child in children)
            {
                ExtractNodeStyle(child);
            }

            return ele;
        }

        public static XmlElement GetOrCreateStyle(XmlElement control)
        {
            var controlClassName = control.Name;
            var styleName = controlClassName + ".Styles";

            var ele = control.GetElementsByTagName(styleName).OfType<XmlElement>().FirstOrDefault();
            if (ele is null)
            {
                ele = control.OwnerDocument.CreateElement(styleName);
                control.InsertBefore(ele, control.FirstChild);
            }
            return ele;
        }

        private static XmlElement CreateStyle(XmlDocument doc,string name, List<XamlPropValue> propValues)
        {
            var ele = doc.CreateElement("Style");
            var selector = doc.CreateAttribute("Selector");
            selector.Value = name;
            ele.Attributes.Append(selector);

            foreach (var propValue in propValues)
            {
                var child = doc.CreateElement("Setter");
                var propertyAtt = doc.CreateAttribute("Property");
                propertyAtt.Value = propValue.Property;

                var valueAtt = doc.CreateAttribute("Value");
                valueAtt.Value = propValue.Value;

                child.Attributes.Append(propertyAtt);
                child.Attributes.Append(valueAtt);

                ele.AppendChild(child);
            }

            return ele;
           
        }

        private static readonly string[] IgnoreAttributes = new string[]
        {
            "Name", "Key", "Classes", "Content", "ItemsSource", "Text"
        };

        private static bool IsIgnoreAttribute(string attributeName)
        {
            if (attributeName.IndexOf(':') > 0) return true;
            return IgnoreAttributes.Any(i => i == attributeName);
        }

        private static readonly string[] ControlNames = new string[]
        {
            "Border", "TextBox", "TextBlock", "Button", "StackPanel", "Grid"
        };

        private static bool IsControl(XmlElement ele)
        {
            var eleName = ele.Name;
            return ControlNames.Any(i => i == eleName);
        }

    }
}
